package storage.impl;

import org.wwx.register.model.ServiceInstance;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;
import storage.ServiceStorage;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 基于 Redis 的服务存储实现
 *
 * @date 2025/6/6
 * @author王闻薪
 */
public class RedisStorage implements ServiceStorage {

    private static final String SERVICE_KEY_PREFIX = "rpc:service:";
    private static final String INSTANCE_KEY_PREFIX = "rpc:instance:";
    private static final String EXPIRY_SET_KEY = "rpc:expiry_set";

    private final JedisPool jedisPool;
    private final int defaultLeaseTime; // 默认租约时间（秒）

    public RedisStorage() {
        this("localhost", 6379, 90); // 默认本地 Redis，端口 6379，租约 90 秒
    }

    public RedisStorage(String host, int port, int defaultLeaseTime) {
        this.defaultLeaseTime = defaultLeaseTime;
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(30);
        poolConfig.setMinIdle(10);
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);

        this.jedisPool = new JedisPool(poolConfig, host, port, 2000);
        System.out.println("RedisStorage initialized with host: " + host + ", port: " + port);
    }

    @Override
    public void registerService(String serviceName, ServiceInstance instance) {
        String serviceKey = SERVICE_KEY_PREFIX + serviceName;
        String instanceKey = INSTANCE_KEY_PREFIX + instance.getId();

        try (Jedis jedis = jedisPool.getResource()) {
            // 开启事务
            Transaction tx = jedis.multi();

            // 1. 将实例添加到服务的实例集合
            tx.hset(serviceKey, instance.getId(), serializeInstance(instance));

            // 2. 存储实例详细信息
            Map<String, String> instanceMap = instanceToMap(instance);
            tx.hmset(instanceKey, instanceMap);

            // 3. 设置实例过期时间
            tx.expire(instanceKey, defaultLeaseTime);

            // 4. 添加到过期集合（用于健康检查）
            tx.zadd(EXPIRY_SET_KEY, System.currentTimeMillis() + (defaultLeaseTime * 1000), instanceKey);

            // 执行事务
            tx.exec();

            System.out.println("Registered service: " + serviceName + " with instance: " + instance.getId());
        } catch (JedisException e) {
            System.err.println("Redis registration failed: " + e.getMessage());
            throw new RuntimeException("Failed to register service in Redis", e);
        }
    }

    @Override
    public void unregisterService(String serviceName, String instanceId) {
        String serviceKey = SERVICE_KEY_PREFIX + serviceName;
        String instanceKey = INSTANCE_KEY_PREFIX + instanceId;

        try (Jedis jedis = jedisPool.getResource()) {
            // 开启事务
            Transaction tx = jedis.multi();

            // 1. 从服务集合中移除实例
            tx.hdel(serviceKey, instanceId);

            // 2. 删除实例详细信息
            tx.del(instanceKey);

            // 3. 从过期集合中移除
            tx.zrem(EXPIRY_SET_KEY, instanceKey);

            // 执行事务
            tx.exec();

            System.out.println("Unregistered service: " + serviceName + " instance: " + instanceId);
        } catch (JedisException e) {
            System.err.println("Redis unregistration failed: " + e.getMessage());
            throw new RuntimeException("Failed to unregister service in Redis", e);
        }
    }

    @Override
    public List<ServiceInstance> getServiceInstances(String serviceName) {
        String serviceKey = SERVICE_KEY_PREFIX + serviceName;

        try (Jedis jedis = jedisPool.getResource()) {
            // 获取服务下所有实例ID
            Map<String, String> instanceMap = jedis.hgetAll(serviceKey);

            List<ServiceInstance> instances = new ArrayList<>();
            for (Map.Entry<String, String> entry : instanceMap.entrySet()) {
                String instanceId = entry.getKey();
                String instanceData = entry.getValue();

                // 检查实例是否仍然有效
                if (jedis.exists(INSTANCE_KEY_PREFIX + instanceId)) {
                    instances.add(deserializeInstance(instanceData));
                }
            }

            // 如果发现无效实例，清理它们
            if (instanceMap.size() > instances.size()) {
                cleanExpiredInstances(serviceName);
            }

            System.out.println("Retrieved " + instances.size() + " instances for service: " + serviceName);
            return instances;
        } catch (JedisException e) {
            System.err.println("Redis service discovery failed: " + e.getMessage());
            throw new RuntimeException("Failed to get service instances from Redis", e);
        }
    }

    @Override
    public void renewLease(String serviceName, String instanceId) {
        String instanceKey = INSTANCE_KEY_PREFIX + instanceId;

        try (Jedis jedis = jedisPool.getResource()) {
            // 1. 延长实例过期时间
            jedis.expire(instanceKey, defaultLeaseTime);

            // 2. 更新过期集合中的时间戳
            long newExpiryTime = System.currentTimeMillis() + (defaultLeaseTime * 1000);
            jedis.zadd(EXPIRY_SET_KEY, newExpiryTime, instanceKey);

            System.out.println("Renewed lease for instance: " + instanceId);
        } catch (JedisException e) {
            System.err.println("Redis lease renewal failed: " + e.getMessage());
            throw new RuntimeException("Failed to renew lease in Redis", e);
        }
    }

    /**
     * 清理过期实例
     */
    public void cleanExpiredInstances() {
        try (Jedis jedis = jedisPool.getResource()) {
            // 获取所有过期的实例键
            long currentTime = System.currentTimeMillis();
            Set<String> expiredInstances = jedis.zrangeByScore(EXPIRY_SET_KEY, 0, currentTime);

            if (expiredInstances.isEmpty()) {
                return;
            }

            System.out.println("Cleaning " + expiredInstances.size() + " expired instances");

            for (String instanceKey : expiredInstances) {
                // 解析服务名和实例ID
                String[] parts = instanceKey.split(":");
                if (parts.length < 3) continue;

                String instanceId = parts[2];
                String serviceName = parseServiceNameFromInstanceKey(instanceKey);

                if (serviceName != null) {
                    unregisterService(serviceName, instanceId);
                }
            }
        } catch (JedisException e) {
            System.err.println("Redis cleanup failed: " + e.getMessage());
        }
    }

    /**
     * 清理指定服务的过期实例
     */
    private void cleanExpiredInstances(String serviceName) {
        String serviceKey = SERVICE_KEY_PREFIX + serviceName;

        try (Jedis jedis = jedisPool.getResource()) {
            // 获取服务下所有实例ID
            Map<String, String> instanceMap = jedis.hgetAll(serviceKey);

            for (String instanceId : instanceMap.keySet()) {
                String instanceKey = INSTANCE_KEY_PREFIX + instanceId;

                // 检查实例是否过期
                if (!jedis.exists(instanceKey)) {
                    jedis.hdel(serviceKey, instanceId);
                    jedis.zrem(EXPIRY_SET_KEY, instanceKey);
                    System.out.println("Cleaned expired instance: " + instanceId);
                }
            }
        }
    }

    // 辅助方法

    private String serializeInstance(ServiceInstance instance) {
        return instance.getId() + "|" + instance.getHost() + "|" + instance.getPort();
    }

    private ServiceInstance deserializeInstance(String data) {
        String[] parts = data.split("\\|");
        if (parts.length < 3) {
            throw new IllegalArgumentException("Invalid instance data: " + data);
        }
        return new ServiceInstance(parts[1], Integer.parseInt(parts[2]));
    }

    private Map<String, String> instanceToMap(ServiceInstance instance) {
        return Map.of(
                "id", instance.getId(),
                "host", instance.getHost(),
                "port", String.valueOf(instance.getPort()),
                "registrationTime", String.valueOf(instance.getRegistrationTime()),
                "lastRenewalTime", String.valueOf(instance.getLastRenewalTime())
        );
    }

    private String parseServiceNameFromInstanceKey(String instanceKey) {
        // 格式: rpc:instance:<instanceId>
        String[] parts = instanceKey.split(":");
        if (parts.length < 3) return null;

        String instanceId = parts[2];
        try (Jedis jedis = jedisPool.getResource()) {
            String instanceData = jedis.hmget(instanceKey, "id", "host", "port").toString();
            // 在实际实现中，需要从实例数据中解析服务名
            // 这里简化处理，实际应用需要存储服务名在实例数据中
            return "default-service";
        }
    }

    /**
     * 关闭 Redis 连接池
     */
    public void shutdown() {
        if (jedisPool != null && !jedisPool.isClosed()) {
            jedisPool.close();
            System.out.println("Redis connection pool closed");
        }
    }
}